<script setup lang="ts">
import * as NaiveUI from 'naive-ui'
import { type PropType, mergeProps, ref, type VNodeProps } from 'vue'
import type { ModelJson } from './DynamicFormTypes'

const props = defineProps({
  formModel: {
    type: Object as PropType<ModelJson>,
    required: true
  },
  formData: {
    type: Object,
    required: true
  }
})

const emit = defineEmits<{
  <T>(event: 'update:value', key: string, value: T): void
}>()

const NaiveUIComponent = (name: string) => NaiveUI[name as keyof typeof NaiveUI]

const createDefaultProps = (key: string) => {
  return {
    value: props.formData[key] || null,
    'onUpdate:value': (value: unknown) => emit('update:value', key, value)
  }
}
const createDatePickerProps = (key: string) => {
  return {
    formattedValue: props.formData[key] || null,
    onUpdateFormattedValue: (value: unknown) => emit('update:value', key, value)
  }
}

const mergeModelValueProps = (type: string, key: string, vp: VNodeProps) => {
  let turnProps: Record<string, unknown> = {}

  switch (type) {
    case 'NDatePicker':
      turnProps = createDatePickerProps(key)
      break

    default:
      turnProps = createDefaultProps(key)
  }

  return mergeProps(turnProps, vp)
}

const concatLimitCharLengthRule = (rule?: NaiveUI.FormItemRule | NaiveUI.FormItemRule[]) => {
  // @ts-ignore
  if (rule?.noInjectLimit) return rule

  const limitRule: NaiveUI.FormItemRule = {
    validator: (_, value) => {
      if (value && value.length > 100) {
        return new Error('输入长度超出限制')
      }
      return true
    },
    trigger: ['blur', 'input'],
    message: '输入长度超出100字符'
  }

  if (!Array.isArray(rule) && rule !== null && typeof rule === 'object') {
    const isEmpty = Object.keys(rule).length === 0
    if (isEmpty) return limitRule
  }

  if (!rule) return limitRule

  return Array.isArray(rule) ? [...rule, limitRule] : [rule, limitRule]
}

const formInst = ref<NaiveUI.FormInst | null>(null)
defineExpose({
  validate: (...args: any[]) => formInst.value?.validate(...args),
  restoreValidation: () => formInst.value?.restoreValidation()
})
</script>

<template>
  <NaiveUI.NConfigProvider
    :locale="NaiveUI.zhCN"
    :date-locale="NaiveUI.dateZhCN"
  >
    <NaiveUI.NForm
      ref="formInst"
      v-bind="$attrs"
    >
      <slot></slot>

      <template v-if="formModel.pages">
        <div
          v-for="(item, i) in formModel.pages"
          :key="i"
          class="form-card"
        >
          <div class="form-card__title">{{ item.title }}</div>
          <div class="grid grid-2">
            <NaiveUI.NFormItem
              v-for="(c, ci) in item.elements"
              :key="ci"
              :path="c.key"
              :rule="concatLimitCharLengthRule(c.rule)"
              :label="c.label"
            >
              <component
                :is="NaiveUIComponent(c.type)"
                v-bind="mergeModelValueProps(c.type, c.key, c.props)"
                :style="c.style"
              />
            </NaiveUI.NFormItem>
          </div>
        </div>
      </template>

      <div
        v-if="formModel.elements"
        class="grid grid-2"
      >
        <NaiveUI.NFormItem
          v-for="(c, ci) in formModel.elements"
          :key="ci"
          :path="c.key"
          :rule="concatLimitCharLengthRule(c.rule)"
          :label="c.label"
        >
          <component
            :is="NaiveUIComponent(c.type)"
            v-bind="mergeModelValueProps(c.type, c.key, c.props)"
            :style="c.style"
          />
        </NaiveUI.NFormItem>
      </div>
    </NaiveUI.NForm>
  </NaiveUI.NConfigProvider>
</template>

<style scoped lang="scss">
.grid {
  display: grid;
  gap: 0 30px;
}

.grid-2 {
  grid-template-columns: repeat(2, 1fr);
}

.form-card {
  padding: 15rem 20rem;

  &__title {
    font-size: 22rem;
    margin-bottom: 15rem;
  }
}
</style>
